package net.microlinktech.testing.packet;

/*import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;*/

import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.TooManyListenersException;

import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
import org.apache.commons.lang3.ArrayUtils;

import net.microlinktech.testing.bean.Meter;
import net.microlinktech.testing.codec.StringCoder;
import net.microlinktech.testing.serial.DL645;
import net.microlinktech.testing.serial.DLMeter;
import net.microlinktech.testing.ui.ReadMeter;
import net.microlinktech.testing.ui.model.MeterTableModel;

public class CommSerialPort implements SerialPortEventListener {

	private DataInputStream input = null;

	private OutputStream output = null;

	@SuppressWarnings("unused")
	private boolean stopped = false;

	@SuppressWarnings("unused")
	private volatile boolean received = false;

	protected SerialPort serialPort;

	@SuppressWarnings("unused")
	private int delayRead;

	private ByteBuffer receiveBuffer;

	private MeterTableModel tableModel;

	public CommSerialPort(SerialPort serialPort, int delayRead, MeterTableModel tableModel)
			throws IOException, TooManyListenersException, UnsupportedCommOperationException {
		this.serialPort = serialPort;
		this.delayRead = delayRead;
		this.tableModel = tableModel;
		this.input = new DataInputStream(serialPort.getInputStream());
		this.output = serialPort.getOutputStream();

		// 侦听到串口有数据,触发串口事件
		this.serialPort.notifyOnDataAvailable(true);
		this.serialPort.notifyOnCTS(true);
		this.serialPort.notifyOnDSR(true);
		this.serialPort.addEventListener(this);
		receiveBuffer = ByteBuffer.allocate(1024);
		receiveBuffer.order(ByteOrder.LITTLE_ENDIAN);
	}

	/**
	 * 发送帧
	 * 
	 * @param message
	 * @param datas
	 */
	public void send(String message, byte[] datas) {
		try {
			output.write(datas);
			output.flush();
			ReadMeter.textArea.append(message + StringCoder.bytesToHexBlankString(datas) + "\r\n");
		} catch (IOException e) {
			ReadMeter.textArea.setText("发送失败！");
			e.printStackTrace();
		}
	}

	/**
	 * CS校验
	 * 
	 * @param buf
	 * @param len
	 * @return
	 */
	public byte checkSum8(byte[] buf, int len) {
		byte sum = 0;
		while ((len--) > 0) {
			sum += buf[len];
		}
		return sum;
	}

	/**
	 * 关闭串口
	 */
	public void close() {
		if (input != null) {
			try {
				input.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (output != null) {
			try {
				output.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		serialPort.close();
	}

	@Override
	public void serialEvent(SerialPortEvent evt) {
		switch (evt.getEventType()) {
		// CTS -清除发送
		case SerialPortEvent.CTS:
			System.out.println("CTS event occured.");
			break;
		// CD -载波检测
		case SerialPortEvent.CD:
			System.out.println("CD event occured.");
			break;
		// BI -通讯中断
		case SerialPortEvent.BI:
			System.out.println("BI event occured.");
			break;
		// DSR -数据设备准备好
		case SerialPortEvent.DSR:
			System.out.println("DSR event occured.");
			break;
		// FE -帧错误
		case SerialPortEvent.FE:
			System.out.println("FE event occured.");
			break;
		// OE -溢位错误
		case SerialPortEvent.OE:
			System.out.println("OE event occured.");
			break;
		// PE -奇偶校验错
		case SerialPortEvent.PE:
			System.out.println("PE event occured.");
			break;
		// RI - 振铃指示
		case SerialPortEvent.RI:
			System.out.println("RI event occured.");
			break;
		// OUTPUT_BUFFER_EMPTY -输出缓冲区已清空
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			System.out.println("OUTPUT_BUFFER_EMPTY event occured.");
			break;
		// DATA_AVAILABLE -有数据到达
		case SerialPortEvent.DATA_AVAILABLE:
			byte ch = 0;
			receiveBuffer.position(0);
			try {
				while (true) {
					ch = input.readByte();
					receiveBuffer.put((byte) ch);
				}
			} catch (Exception e) {
				// e.printStackTrace();
			}

//			parsePacket(receiveBuffer);//645协议

			parsePacket376_2(receiveBuffer);
			break;
		}
	}

	/**
	 * 解析收到的数据包
	 * 
	 * @param byteBuffer
	 */
	@SuppressWarnings("deprecation")
	public void parsePacket(ByteBuffer byteBuffer) {

		// long startTime=System.currentTimeMillis();
		byte[] receiveDatas = byteBuffer.array();
		int length = byteBuffer.position();
		String out = StringCoder.bytesToHexString(receiveDatas, length, " ");
		Timestamp time = new Timestamp(new Date().getTime());
		final SimpleDateFormat FORMAT_TIME = new SimpleDateFormat("HH:mm:ss");
		ReadMeter.textArea.append("[" + FORMAT_TIME.format(time) + " 串口接收数据：]: " + byteBuffer.position()
				+ "字节\r\n" + out + "\r\n");
		ReadMeter.textArea.setCaretPosition(ReadMeter.textArea.getText().length());
		byteBuffer.position(0);
		byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
		byte mBegin = byteBuffer.get(); // 帧 开始符
		if (mBegin == DLMeter.M_BEGIN) {

			byte cmd = 0x00;
			byte boardNum = byteBuffer.get(); // 板子编号
			byte dauNum = byteBuffer.get(); // dau编号
			byte[] dauAddress = { boardNum, dauNum };
			byte mBegin2 = byteBuffer.get(); // 帧 开始符
			if (mBegin2 == DLMeter.M_BEGIN) {
				byteBuffer.get(); // 帧 长度
				cmd = byteBuffer.get(); // 帧 命令标识
				if (cmd == 0x04) {
					// ////////// 645帧的数据区 ////////////

					byte begin = (byte) byteBuffer.get();
					if (begin == DL645.POS_BEGIN) {
						byte[] address = new byte[6];
						byteBuffer.get(address);
						ArrayUtils.reverse(address);
						Meter meter = getByAddress(boardNum, dauNum, address);
						if (meter != null) {

							byte center = (byte) byteBuffer.get();
							if (center == DL645.POS_BEGIN) {
								byte code = (byte) byteBuffer.get();
								if (code == 0x11) {
									byteBuffer.get();
									byte[] dataIdents = new byte[4];
									byteBuffer.get(dataIdents);
									ArrayUtils.reverse(address);
									DICoder diCoder = new DICoder(meter, address, dataIdents);
									byte[] responseDatas = diCoder.reponsePacket();
									if (responseDatas != null) {
										String message = diCoder.getMessage();
										send(message, AutoMeterFrame(dauAddress, cmd, responseDatas));
									}
								}
							}
						} else {
							ReadMeter.textArea.append("该电表不存在！");
							return;
						}

					}

				} /*
				 * else if (cmd == 0x05) { byte[] dataTmp = {}; send("响应帧为：   ",
				 * AutoMeterFrame(dauAddress, cmd, dataTmp)); }
				 */
			}

			// long timeInterval=System.currentTimeMillis()-startTime;
			// System.out.println(timeInterval);
		}

	}


	//376.2协议解析
	@SuppressWarnings("deprecation")
	public void parsePacket376_2(ByteBuffer byteBuffer) {

		// long startTime=System.currentTimeMillis();
		byte[] receiveDatas = byteBuffer.array();
		int length = byteBuffer.position();
		String out = StringCoder.bytesToHexString(receiveDatas, length, " ");
		Timestamp time = new Timestamp(new Date().getTime());
		final SimpleDateFormat FORMAT_TIME = new SimpleDateFormat("HH:mm:ss");
		ReadMeter.textArea.append("[" + FORMAT_TIME.format(time) + " 串口接收数据：]: " + byteBuffer.position()
				+ "字节\r\n" + out + "\r\n");
		ReadMeter.textArea.setCaretPosition(ReadMeter.textArea.getText().length());
		byteBuffer.position(0);
		byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
		byte mBegin = byteBuffer.get(); // 帧 开始符
		if (mBegin == DLMeter.M_BEGIN) {

			byte cmd = 0x00;
			byte boardNum = byteBuffer.get(); // 板子编号
			byte dauNum = byteBuffer.get(); // dau编号
			byte versionNo = byteBuffer.get(); // 版本号
			String versionStr = "";

			byte[] dauAddress = { boardNum, dauNum };
			byte mBegin2 = byteBuffer.get(); // 帧 开始符
			if (mBegin2 == DLMeter.M_BEGIN) {
				byteBuffer.get(); // 帧 长度
				cmd = byteBuffer.get(); // 帧 命令标识
				if (cmd == 0x04) {
					// ////////// 645帧的数据区 ////////////

					byte begin = (byte) byteBuffer.get();
					if (begin == DL645.POS_BEGIN) {
						byte[] address = new byte[6];
						byteBuffer.get(address);
						ArrayUtils.reverse(address);
						Meter meter = getByAddress376_2(address, versionStr);
						if (meter != null) {

							byte center = (byte) byteBuffer.get();
							if (center == DL645.POS_BEGIN) {
								byte code = (byte) byteBuffer.get();
								if (code == 0x11) {
									byteBuffer.get();
									byte[] dataIdents = new byte[4];
									byteBuffer.get(dataIdents);
									ArrayUtils.reverse(address);
									DICoder diCoder = new DICoder(meter, address, dataIdents);
									byte[] responseDatas = diCoder.reponsePacket();
									if (responseDatas != null) {
										String message = diCoder.getMessage();
										send(message, AutoMeterFrame(dauAddress, cmd, responseDatas));
									}
								}
							}
						} else {
							ReadMeter.textArea.append("该电表不存在！");
							return;
						}

					}

				} /*
				 * else if (cmd == 0x05) { byte[] dataTmp = {}; send("响应帧为：   ",
				 * AutoMeterFrame(dauAddress, cmd, dataTmp)); }
				 */
			}

			// long timeInterval=System.currentTimeMillis()-startTime;
			// System.out.println(timeInterval);
		}

	}


	/**
	 * 用虚拟电表帧结构组包发送
	 * 
	 * @param data
	 * @return
	 */
	public byte[] AutoMeterFrame(byte[] dauAddress, byte cmd, byte[] data) {
		byte[] meterFrame = new byte[1 + 2 + 1 + 1 + 1 + data.length + 1 + 1];
		meterFrame[0] = 0x70;
		System.arraycopy(dauAddress, 0, meterFrame, 1, dauAddress.length);
		meterFrame[3] = 0x70;
		meterFrame[4] = (byte) data.length;
		if (meterFrame[4] == 0) {
			meterFrame[5] = cmd;
			meterFrame[6] = checkSum8(meterFrame, 6);
			meterFrame[7] = 0x18;
		} else {
			meterFrame[5] = cmd;
			System.arraycopy(data, 0, meterFrame, 6, data.length);
			meterFrame[6 + data.length] = checkSum8(meterFrame, 6 + data.length);
			meterFrame[6 + data.length + 1] = 0x18;
		}
		return meterFrame;
	}

	/**
	 * 根据板子编号、dau编号以及电表地址获取一条list电表数据
	 * 
	 *
	 * @return
	 */
	private Meter getByAddress(int boardNum, int dauNum, byte[] address) {
		List<Meter> meters = tableModel.getMeters();
		String addressStr = StringCoder.bytesToHexString(address);
		for (Meter meter : meters) {
			/*if (meter.getBoardNum() == boardNum && meter.getDauNum() == dauNum
					&& addressStr.equals(meter.getMeterAddress())) {
				return meter;
			}*/
		}
		return null;
	}
	private Meter getByAddress376_2(byte[] nodeAddress, String  versionNo) {
		List<Meter> meters = tableModel.getMeters();
		String addressStr = StringCoder.bytesToHexString(nodeAddress);
		for (Meter meter : meters) {
			if (Objects.equals(meter.getVersionNo(), versionNo) && addressStr.equals(meter.getNodeAddress())) {
				return meter;
			}
		}
		return null;
	}

	public SerialPort getSerialPort() {
		return serialPort;
	}

}
